﻿/*
#if !defined(_CXXL3D_SSE)
#include <Math\MATH_CPU.HPP>
#else
#endif
*/

#include <Sys\MATH.HPP>
#include <Sys\C3DNODE.HPP>
#include <Sys\CAMERA.HPP>
#include <Sys\CSMART_PTR.HPP>

using namespace CxxlMan;
using namespace Cxxl3D;

// 元件介面的識別碼
#define CCamera_ClassID L"A9D9D6F6-4C18-42ac-A7EB-7A43F2F0A97F"

cxxlObject *new_CCamera();
cxxlPreserve2 *Regenera_CCamera(const wchar_t *ClassID);



// ICamera 的實作
class CCamera:public ICamera
{

  // 投影
  union
  {
    C3D_FLOAT pj[3];
    struct
    {
      union
      {
        C3D_FLOAT pj_fovy;
        C3D_FLOAT pj_h;
      };
    
      C3D_FLOAT pj_z_near;
      C3D_FLOAT pj_z_far;
    };
  };

  // Viewport
 	union
  {
    C3D_FLOAT vp[4];
    struct
    {
      C3D_FLOAT vp_Left;
      C3D_FLOAT vp_Top;
      C3D_FLOAT vp_Right;
      C3D_FLOAT vp_Bottom;
    };
  };

  CSmart_Ptr<Matrix4x4> m_ProjMatrix_cPtr; // 由渲染系統寄放的投影轉換矩陣

  bool m_isPerspective;  // 指定 Perspective 投影模式為 true



  // 若採用 Perspective 投影模式回覆 true
  virtual bool cxxlFASTCALL isPerspective()
  {
    return m_isPerspective;
  }



  // 設定 Perspective 投影模式
  // fovy = 垂直方向的視角
  // z_hear = 鏡頭可以看到的最近距離
  // z_far = 鏡頭可以看到的最遠距離
  virtual void cxxlFASTCALL SetPerspective(C3D_FLOAT fovy, C3D_FLOAT z_near, C3D_FLOAT z_far)
  {
    csPreserve.Enter();

    pj_fovy = fovy;
    pj_z_near = z_near;
    pj_z_far = z_far;

    m_isPerspective = true;

    csPreserve.Leave();
  }

  // 若不是 Perspective 回覆 false
  virtual bool cxxlFASTCALL GetPerspective(C3D_FLOAT &fovy, C3D_FLOAT &z_near, C3D_FLOAT &z_far)
  {
    CriticalSecHandle AutoCSH(csPreserve);

    if(m_isPerspective == false)
      return false;

    fovy = pj_fovy;
    z_near = pj_z_near;
    z_far = pj_z_far;

    return true;
  }

  // 設定/取得 Ortho 投影模式
  // h = 鏡頭垂直方向可以看到的範圍
  // z_hear = 鏡頭可以看到的最近距離
  // z_far  = 鏡頭可以看到的最遠距離
  virtual void cxxlFASTCALL SetOrtho(C3D_FLOAT h, C3D_FLOAT z_near, C3D_FLOAT z_far)
  {
    csPreserve.Enter();

    pj_h = h;
    pj_z_near = z_near;
    pj_z_far = z_far;

    m_isPerspective = false;

    csPreserve.Leave();
  }

  // 若不是 Ortho 回覆 false
  virtual bool cxxlFASTCALL GetOrtho(C3D_FLOAT &h, C3D_FLOAT &z_near, C3D_FLOAT &z_far)
  {
    CriticalSecHandle AutoCSH(csPreserve);

    if(m_isPerspective == true)
      return false;

    h = pj_h;
    z_near = pj_z_near;
    z_far = pj_z_far;

    return true;
  }


  // 指定 Virwport 的範圍，值在 0 ~ 1 之間(包括 0、1)，若未指定，實作應內定(0,0)-(1,1)
  virtual void cxxlFASTCALL SetViewport(float Left, float Top, float Right, float Bottom)
  {
    csPreserve.Enter();

    vp_Left = Left;
    vp_Top = Top;
    vp_Right = Right;
    vp_Bottom =Bottom;

    csPreserve.Leave();
  }

  // 取得 Virwport 的範圍
  virtual void cxxlFASTCALL GetViewport(float &Left, float &Top, float &Right, float &Bottom) const
  {
    csPreserve.Enter();

    Left = vp_Left;
    Top = vp_Top;
    Right = vp_Right;
    Bottom = vp_Bottom;

    csPreserve.Leave();
  }

  // 取得觀察座標空間的轉換矩陣
  virtual CSmart_Ptr<Matrix4x4> cxxlFASTCALL GetViewMatrix() const
  {
    CriticalSecHandle AutoCSH(csPreserve);

    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = Get_IC3D_Node();

    if(IC3D_Node_Ptr.isNULL())
      return NULL;

    CSmart_Ptr<const Matrix4x4> WorldMatrix_Ptr = IC3D_Node_Ptr->GetWorldMatrix();


    return new Matrix4x4( CoordinateInvert( *(const Matrix4x4*)WorldMatrix_Ptr ) );
  }


protected:

  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && ICamera::Ref(F))
    {
      if(F->Ref(pj, 3, L"CCamera.Projection") == false) 
        return false;

      if(F->Ref(vp, 4, L"CCamera.Viewport") == false) 
        return false;

      if(F->Ref(&m_isPerspective, 1, L"CCamera.isPerspective") == false) 
        return false;

      return true;
    }
    else
      return false;
  }

public:

  // Constructor 建構時用
  CCamera()
    :cxxlObject(CxxlMan::Spirit_Easy),
    cxxlPreserve2(CCamera_ClassID),
    m_ProjMatrix_cPtr(NULL)
  {
    pj_fovy = pj_z_near = pj_z_far = vp_Left = 0;
    vp_Top = vp_Left = 0;
    vp_Right = vp_Bottom = 1;

    m_isPerspective = false;
  }


  friend cxxlObject *new_CCamera();
  friend cxxlPreserve2 *Regenera_CCamera(const wchar_t *ClassID);

};


cxxlObject *new_CCamera()
{
  return new CCamera();
}

cxxlPreserve2 *Regenera_CCamera(const wchar_t *ClassID)
{
  if(wcscmp(CCamera_ClassID, ClassID) == 0)  // 只處理最終實作類別的識別碼就行了
    return new CCamera();
  
  return NULL;
}

/*
extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID, void *Arg,   ISpirit *spirit)
{
  if(wcscmp(ICamera_ClassID, ClassID) == 0)  // 只處理介面就好
    return new CCamera();

  return NULL;
}

extern "C" CXXL_DLLEXPORT
cxxlPreserve2 * cxxlCDECL CxxlMan_Preserve2Factory(const wchar_t *ClassID)
{
  if(wcscmp(CCamera_ClassID, ClassID) == 0)  // 只處理最終實作類別的識別碼就行了
    return new CCamera();
  
  return NULL;
}
*/